// Have include guard.
#ifndef FPK_THREAD_HPP
#define FPK_THREAD_HPP

// Include files.
#include <string>
#include "WPILib.h"
#include "Any.hpp"

namespace Internal
{
	template<typename Argument>
	class Receiver
	{
		public:
			static Argument Receive(UINT32 arg)
			{
				Argument *ptr = Receiver<Argument*>::Receive(arg);
				Argument result(*ptr);
				
				delete ptr;
				return result;
			}
	};
	
	template<typename ArgumentBase>
	class Receiver<ArgumentBase*>
	{
		public:
			static ArgumentBase *Receive(UINT32 arg)
			{
				return (ArgumentBase*)(arg);
			}
	};
	
	template<typename ArgumentBase>
	class Receiver<ArgumentBase&>
	{
		public:
			static ArgumentBase &Receive(UINT32 arg)
			{
				return *(Receiver<ArgumentBase*>::Receive(arg));
			}
	};
	
	template<>
	class Receiver<UINT32>
	{
		public:
			static UINT32 Receive(UINT32 arg)
			{
				return arg;
			}
	};
}

/**
 *  The ThreadBase class is the base class for all Threads.
 */
class ThreadBase
{
	template<typename Function>
	friend class Thread;
	
	friend class Thread<Any>;
	
	private:
		static UINT32 m_numberNext;
		
		inline static UINT32 Pass(UINT32 argument)
		{
			return argument;
		}
		
		template<typename ArgumentBase>
		static UINT32 Pass(ArgumentBase *argument)
		{
			return Pass(UINT32(argument)); 
		}
		
		template<typename ArgumentBase>
		static UINT32 Pass(ArgumentBase &argument)
		{
			return Pass(&argument);
		}
		
		template<typename Argument>
		static UINT32 Pass(Argument argument)
		{
			Argument *arg = new Argument(argument);
			return Pass(arg);
		}
		
		template<typename Argument>
		static Argument Receive(UINT32 arg)
		{
			return Internal::Receiver<Argument>::Receive(arg);
		}
		
		Task m_task;
		
		template<typename Function>
		ThreadBase(const std::string &name, Function function) :
			m_task(name.c_str(), FUNCPTR(function))
		{
			// TODO: Allow default name using m_numberNext.
			m_numberNext++;
		}
	public:
		/**
		 *  Get whether the Thread is suspended.
		 *  
		 *  @returns True if the Thread is suspended or false if otherwise.
		 */
		bool GetIsSuspended()
		{
			return m_task.IsSuspended();
		}
		
		/**
		 *  Stop the Thread.
		 *  
		 *  @return If the Thread exists and was successfully stopped.
		 */
		bool Stop()
		{
			return m_task.Stop();
		}
		
		/**
		 *  Suspend the Thread.
		 *  
		 *  @return If the Thread exists and was successfully suspended.
		 */
		bool Suspend()
		{
			return m_task.Suspend();
		}
		
		/**
		 *  Resume the Thread.
		 *  
		 *  @return If the Thread exists and was successfully resumed.
		 */
		bool Resume()
		{
			return m_task.Resume();
		}
};

/**
 *  The Thread template class lets you launch a function on a seperate thread.
 *  
 *  This is a bit more difficult to use than Thread<Any>, but a lot safer.
 *  
 *  @tparam Function The type of the function (e.g. For a parameterless function that returns void "void *()"). (Leaving this out will make it default to Thread<Any>.)
 */
template<typename Function = Any>
class Thread;

/**
 *  The Thread<Any> class is an easy to use class that lets you launch a function on a seperate thread.
 *  
 *  Any function can be taken in the constructor, but you must make sure the argument count and types passed to Start match those of the given function.
 */
template<>
class Thread<Any>
{
	private:
		class IInternalThreadLauncher
		{
			public:
				virtual void Start(Task &task, UINT32 arg0 = 0, UINT32 arg1 = 0, UINT32 arg2 = 0) = 0;
		};
		
		template<typename Argument>
		static UINT32 Pass(Argument arg)
		{
			return ThreadBase::Pass<Argument>((Argument)arg);
		}
		
		template<typename Function>
		class InternalThreadLauncher :
			public IInternalThreadLauncher
		{
			private:
				const Function m_function;
			public:
				InternalThreadLauncher(Function function) :
					m_function(function)
				{
				}
				
				virtual void Start(Task &task, UINT32 arg0 = 0, UINT32 arg1 = 0, UINT32 arg2 = 0)
				{
					task.Start(Thread<Any>::Pass<Function>(m_function), arg0, arg1, arg2);
				}
		};
		
		ThreadBase *const m_thread;
		IInternalThreadLauncher *const m_launcher;
	public:
		/**
		 *  Constructor for Thread<Any>.
		 *  
		 *  @tparam Function The type of the function. (C++ can infer the correct type automatically if you don't specify.)
		 *  @param name The name of the thread.
		 *  @param function The function to be run by this Thread.
		 */
		template<typename Function>
		Thread(const std::string &name, Function function) :
			m_thread(new Thread<Function>(name, function)),
			m_launcher(new InternalThreadLauncher<Function>(function))
		{
		}
		
		/**
		 *  Destructor for Thread<Any>.
		 */
		~Thread();
		
		/**
		 *  Start the Thread.
		 *  
		 *  This overlaod passes no arguments.
		 */
		void Start()
		{
			m_launcher->Start(m_thread->m_task);
		}
		
		/**
		 *  Start the Thread.
		 *  
		 *  @tparam Arg0 The type of the first argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @param arg0 The first argument.
		 */
		template<typename Arg0>
		void Start(Arg0 arg0)
		{
			m_launcher->Start(m_thread->m_task, ThreadBase::Pass(arg0));
		}
		
		/**
		 *  Start the Thread.
		 *  
		 *  @tparam Arg0 The type of the first argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @tparam Arg1 The type of the second argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @param arg0 The first argument.
		 *  @param arg1 The second argument.
		 */
		template<typename Arg0, typename Arg1>
		void Start(Arg0 arg0, Arg1 arg1)
		{
			m_launcher->Start(m_thread->m_task, ThreadBase::Pass(arg0), ThreadBase::Pass(arg1));
		}
		
		/**
		 *  Start the Thread.
		 *  
		 *  @tparam Arg0 The type of the first argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @tparam Arg1 The type of the second argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @tparam Arg2 The type of the third argument. (C++ can infer the correct type automatically if you don't specify.)
		 *  @param arg0 The first argument.
		 *  @param arg1 The second argument.
		 *  @param arg2 The third argument.
		 */
		template<typename Arg0, typename Arg1, typename Arg2>
		void Start(Arg0 arg0, Arg1 arg1, Arg2 arg2)
		{
			m_launcher->Start(m_thread->m_task, ThreadBase::Pass(arg0), ThreadBase::Pass(arg1), ThreadBase::Pass(arg2));
		}
		
		bool GetIsSuspended();
		bool Stop();
		bool Suspend();
		bool Resume();
};

#define FPK_THREAD_INTERNAL_THIS_HAS 0
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 0
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 0
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 1
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 0
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 2
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 0
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 3
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 1
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 0
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 1
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 1
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 1
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 2
#include "ThreadTemplate.hpp"
#define FPK_THREAD_INTERNAL_THIS_HAS 1
#define FPK_THREAD_INTERNAL_ARGS_NUMBER 3
#include "ThreadTemplate.hpp"

/*
template<>
class Thread<void *()> :
	public ThreadBase
{
	typedef void (*Function)();
	typedef void (*FirstFunction)(UINT32);
	
	private:
		const Function m_function;
		
		static void Run(UINT32 function)
		{
			Receive<Function>(function)();
		}
	public:
		Thread(const std::string &name, Function function) :
			ThreadBase(name, Run),
			m_function(function)
		{
		}
		
		void Start()
		{
			m_task.Start(Pass(m_function));
		}
};

template<typename Arg0>
class Thread<void *(Arg0)> :
	public ThreadBase
{
	typedef void (*Function)(Arg0);
	typedef void (*FirstFunction)(UINT32, UINT32);
	
	private:
		const Function m_function;
		
		static void Run(UINT32 function, UINT32 arg0)
		{
			Receive<Function>(function)(Receive<Arg0>(arg0));
		}
	public:
		Thread(const std::string &name, Function function) :
			ThreadBase(name, Run),
			m_function(function)
		{
		}
		
		void Start(Arg0 arg0)
		{
			m_task.Start(Pass(m_function), Pass(arg0));
		}
};

template<typename ArgThis>
class Thread<void (ArgThis::*)()> :
	public ThreadBase
{
	typedef void (ArgThis::*Function)();
	typedef void (*FirstFunction)(UINT32, UINT32);
	
	private:
		const Function m_function;
		
		static void Run(UINT32 function, UINT32 argThis)
		{
			Receive<ArgThis>(argThis).*(Receive<Function>(function))();
		}
	public:
		Thread(const std::string &name, Function function) :
			ThreadBase(name, Run),
			m_function(function)
		{
		}
		
		void Start(ArgThis argThis)
		{
			m_task.Start(Pass(m_function), Pass(argThis));
		}
};

template<typename ArgThis, typename Arg0>
class Thread<void (ArgThis::*)(Arg0)> :
	public ThreadBase
{
	typedef void (ArgThis::*Function)(Arg0);
	typedef void (*FirstFunction)(UINT32, UINT32, UINT32);
	
	private:
		const Function m_function;
		
		static void Run(UINT32 function, UINT32 argThis, UINT32 arg0)
		{
			Receive<ArgThis>(argThis).*(Receive<Function>(function))(Receive<Arg0>(arg0));
		}
	public:
		Thread(const std::string &name, Function function) :
			ThreadBase(name, Run),
			m_function(function)
		{
		}
		
		void Start(ArgThis argThis, Arg0 arg0)
		{
			m_task.Start(Pass(m_function), Pass(argThis), Pass(arg0));
		}
};
*/

#endif // #ifndef FPK_THREAD_HPP
